'''
This module defines the basic command structure, and provides a number of
classes which work with commands.
'''

import cmdbase as _c
import copy
from weakref import ref, WeakKeyDictionary

import xml.etree.ElementTree as et

# ---------------------------------------------------------------------------
#  Event handling
# ---------------------------------------------------------------------------

class event(object):
   '''Decorator class. This is a placeholder object which generates an Event
   on its first call. The Event is the places in the owners __dict__ instead of
   this.
   '''

   def __init__(self, func):
      self.func = func

   def __get__(self, instance, cls):
      if isinstance(instance, cls):
         evt = Event()
         evt.primary = self.func
         evt.owner = instance
         instance.__dict__[self.func.__name__] = evt
         return evt


class Event(object):
   '''An event class. The event may be bound to any number of functions. When
   the event in called, each function will be called in the order in which they
   were bound. If any of the called functions returns False, the event call will
   return False. This may be used to determine whether to continue with an
   action.

   The Event class can also be used as function decorator, so that when the
   function in called each bound function will first be called with the same
   arguments as the decorated function. If any of the calls return False, the
   decorated function will not be executed.

   Note that a return value of None will be seen as True.

   e.g.
   >>> class A:
   ...   def __init__(self):
   ...     self.evt = Event()
   ...
   ...   def callevt(self, msg):
   ...     self.evt(msg=msg)
   ...
   ...   @event
   ...   def action(self, msg):
   ...     print 'A:', msg
   ...
   >>> class B:
   ...   def __init__(self, a):
   ...     a.evt.bind(self.on_evt)
   ...     a.action.bind(self.on_action)
   ...
   ...   def on_evt(self, msg):
   ...     print 'B:', msg
   ...
   ...   def on_action(self, msg):
   ...     print 'this is also an action:', msg
   ...
   >>> a = A()
   >>> b = B(a)
   >>> a.callevt('hello')
   B: hello
   >>> a.action('Action')
   this is also an action: Action
   A: Action

   Events can also be treated as boolean conditions. I.e. an event is seen as
   valid until its reciprocal is called:
   >>> a = Event()
   >>> b = ~a
   >>> a == ~b
   True
   >>> v = a()
   >>> a.valid
   True

   AND is supported. E.g. r = a and B:
   call     a.valid  b.valid  r.valid  r_call
   a()      True     False    False    -
   b()      True     True     True     r()
   a()      True     True     True     -
   not b()  True     False    False    not r()

   OR is also supported. For r = a or b:
   call     a.valid  b.valid  r.valid  r_call
   a()      True     False    True     r()
   b()      True     True     True     -
   a()      True     True     True     -
   not a()  False    True     True     -
   not b()  False    False    False    not r()
   '''

   def __init__(self, func=None, not_func=None):
      self.targets = []
      self.primary = None
      self.owner = None
      self._not = None
      self._valid = False

   valid = property(lambda self: self._valid)

   def _setinvalid(self, *args, **kwargs):
      self._valid = False

   def bind(self, onfunc, offfunc=None):
      on = onfunc if isinstance(onfunc, tuple) else (onfunc,)
      off = offfunc if isinstance(offfunc, tuple) else (offfunc,)
      
      # To prevent looping
      assert self not in on
      assert self not in off
      
      self.targets += on
      if off[0] is not None:
         (~self).bind(off)

   def unbind(self, *funcs):
      for target in funcs:
         self.targets.remove(target)

   def link(self, event):
      '''links event to this event, binding both event and ~event.'''
      assert isinstance(event, Event)
      self.bind(event)
      (~self).bind(~event)

   def unlink(self, event):
      '''unlinks event to this event, unbinding both event and ~event.'''
      assert isinstance(event, Event)
      self.unbind(event)
      (~self).unbind(~event)

   def __call__(self, *args, **kwargs):
      assert self not in self.targets
      accept = True
      self._valid = True
      for target in self.targets:
         accept = (target(*args, **kwargs) != False)
      if self.owner is not None and accept:
         try:
            return self.primary(self.owner, *args, **kwargs)
         except TypeError:
            raise TypeError, 'arguments incompatible with %s: %s, %s' % (self.primary, args, kwargs)
      else:
         return accept

   def __get__(self, instance, cls):
      if self.owner is None and instance is not None:
         evt = Event()
         evt.primary = self.primary
         evt.targets = copy.copy(self.targets)
         evt.owner = instance
         return evt
      elif self.owner is instance:
         return self
      else:
         raise TypeError, 'Invalid owner of Event: %s != %s' % (instance, self.owner)

   def __invert__(self):
      '''Return an event that is seen as the reciprocal of this event.'''
      if self._not is None:
         self._not = Event()
         self._not._not = self
         self._not.bind(self._setinvalid)
      return self._not

   def __and__(self, other):
      '''Return an event that will only be triggered when both events are triggered.'''
      return _AndEvent(self, other)

   def __or__(self, other):
      '''Return an event that will be triggered when either event is triggered.'''
      return _OrEvent(self, other)


class _AndEvent(Event):
   '''The AND of two events:
   >>> a = Event()
   >>> b = Event()
   >>> r = a & b
   >>> def rcall():
   ...   print 'r()'
   >>> def notrcall():
   ...   print 'not r()'
   >>> r.bind(rcall, notrcall)
   >>> v = a()
   >>> print a.valid, b.valid, r.valid
   True False False
   >>> v = b()
   r()
   >>> print a.valid, b.valid, r.valid
   True True True
   >>> v = a()
   >>> print a.valid, b.valid, r.valid
   True True True
   >>> v = (~b)()
   not r()
   >>> print a.valid, b.valid, r.valid
   True False False
   '''

   def __init__(self, evt1, evt2):
      Event.__init__(self)
      self.evt1, self.evt2 = evt1, evt2
      evt1.bind(self.__t1, (self.__nt1, self._setinvalid))
      evt2.bind(self.__t2, (self.__nt2, self._setinvalid))
      self.__c1 = self.__c2 = None

   def __t1(self, *args, **kwargs):
      if self.__c2 is not None:
         kwargs.update(self.__c2[1])
         self.__call__(*(args + self.__c2[0]), **kwargs)
         self.__c2 = self.__c1 = None
      else:
         self.__c1 = (args, kwargs)

   def __t2(self, *args, **kwargs):
      if self.__c1 is not None:
         self.__c1[1].update(kwargs)
         self.__call__(*(self.__c1[0] + args), **self.__c1[1])
         self.__c1 = self.__c2 = None
      else:
         self.__c2 = (args, kwargs)

   def __nt1(self, *args, **kwargs):
      self.__c1 = None
      (~self)()

   def __nt2(self, *args, **kwargs):
      self.__c2 = None
      (~self)()


class _OrEvent(Event):
   '''The OR of two events.
   For r = a or b:
   call     a.valid  b.valid  r.valid  r_call
   a()      True     False    True     r()
   b()      True     True     True     -
   a()      True     True     True     -
   not a()  False    True     True     -
   not b()  False    False    False    not r()

   >>> a = Event()
   >>> b = Event()
   >>> r = a | b
   >>> def rcall():
   ...   print 'r()'
   >>> def notrcall():
   ...   print 'not r()'
   >>> r.bind(rcall)
   >>> (~r).bind(notrcall)
   >>> v = a()
   r()
   >>> print a.valid, b.valid, r.valid
   True False True
   >>> v = b()
   >>> print a.valid, b.valid, r.valid
   True True True
   >>> v = a()
   >>> print a.valid, b.valid, r.valid
   True True True
   >>> v = (~a)()
   >>> print a.valid, b.valid, r.valid
   False True True
   >>> v = (~b)()
   not r()
   >>> print a.valid, b.valid, r.valid
   False False False
   '''

   def __init__(self, evt1, evt2):
      Event.__init__(self)
      self.evt1, self.evt2 = evt1, evt2
      evt1.bind(self.__t1, self.__nt1)
      evt2.bind(self.__t2, self.__nt2)
      self.__valid1 = self.__valid2 = None

   def __t1(self, *args, **kwargs):
      if not self.valid:
         self.__valid1 = True
         self.__call__(*args, **kwargs)

   def __t2(self, *args, **kwargs):
      if not self.valid:
         self.__valid2 = True
         self.__call__(*args, **kwargs)

   def __nt1(self, *args, **kwargs):
      self.__valid1 = False
      if self.__valid1 == self.__valid2 == False:
         self._valid = False
         (~self)()

   def __nt2(self, *args, **kwargs):
      self.__valid2 = False
      if self.__valid1 == self.__valid2 == False:
         self._valid = False
         (~self)()

      
# ---------------------------------------------------------------------------
#  Command controlled dictionaries
# ---------------------------------------------------------------------------

class CDict(object):
   '''A command handled dict. This provides all the functionality of a python
   dict, but any changes made to it are stored in the command system.
   Sanity check:
   >>> a = CDict({'a':1, 's':2})
   >>> a == eval(repr(a))
   True
   >>> s = pickle.dumps(a)
   '''
   
   __slots__ = ['_CDict__d']
   
   def __init__(self, *args, **kwargs):
      object.__init__(self)
      self.__d = dict(*args, **kwargs)

   def clear(self):
      for key in self.iterkeys():
         self.delitem(key)

   def __contains__(self, key):
      return key in self.__d

   def copy(self):
      return copy.copy(self)

   def __delitem__(self, key):
      _c.DeleteCommand('Delete Item', self.__d, key)

   def __eq__(self, var):
      return self.__d == var

   def get(self, key, value):
      return self.__d.get(key, value)

   def __getitem__(self, key):
      return self.__d[key]

   def items(self):
      return CList(self.__d.items())

   def iteritems(self):
      return self.__d.iteritems()

   def iterkeys(self):
      return self.__d.iterkeys()

   def itervalues(self):
      return self.__d.itervalues()

   def keys(self):
      return CList(self.__d.keys())

   def __len__(self):
      return len(self.__d)

   def pop(self, key, value=None):
      v = self[key] if key in self else value
      if key in self: self.__delitem__(key)
      return v

   def __repr__(self):
      return 'CDict(%s)' % repr(self.__d)

   def setdefault(self, key, value=None):
      if key not in self:
         self.__setitem__(key, value)
      return self[key]

   def __setitem__(self, key, value):
      _c.ChangeDictCommand('Change item', self.__d, key, value)

   def update(self, b):
      for key, value in b.iteritems():
         self.__setitem__(key, value)

   def values(self):
      return self.__d.values()



class CControlDict(object):
   '''A CDict whose key values are controlled by an external sequence passed to
   the initialiser. The sequence must provide one of the following methods:

      iteritems()    An iterator over (key, value) pairs
         OR
      __iter__()     An iterator over items.

   The argument newfunc is a function which is called to generate a new object when required.

   e.g.
   >>> link = CList(['a', 'b'])
   >>> a = CControlDict(link, 0)
   >>> a.linkdefs()
   {'a': 0, 'b': 0}
   >>> a['b']
   0
   >>> try:
   ...   a['c'] = 4
   ... except:
   ...   print 'Exception'
   ...
   Exception
   >>> link.append('c')
   'c'
   >>> len(a)
   3
   >>> a['c'] = 5
   >>> a
   CControlDict({'a': 0, 'c': 5, 'b': 0})
   >>> b = CControlDict(a)
   >>> b['c']
   5
   >>> b['c'] = 4
   >>> a['c'], b['c']
   (5, 4)
   >>> del link[-1]
   >>> try:
   ...   print b['c']
   ... except:
   ...   print 'Exception'
   ...
   Exception
   >>> b
   CControlDict({'a': 0, 'b': 0})
   '''

   def __init__(self, linked, newfunc):
      object.__init__(self)
      self.linked = linked
      self.newfunc = newfunc
      self.__d = CDict(dict.fromkeys(self.linked, self.newfunc()))

   def __contains__(self, key):
      return key in self.keys()

   def copy(self):
      return copy.copy(self)

   def __delitem__(self, key):
      if key not in self.keys():
         del self.__d[key]
      else:
         raise KeyError, 'key not in linked object'

   def __eq__(self, var):
      return self.__d == var

   def __getitem__(self, key):
      if key in self.keys():
         return self.__d.setdefault(key, self.newfunc())
      else:
         raise KeyError, 'key not in linked object'
         
   def index(self, value):
      for k, v in self.iteritems():
         if v == value:
            return k
      return None

   def items(self):
      return [ self.__getitem__(key) for key in self.keys() ]

   def iteritems(self):
      return [ (key, self.__getitem__(key)) for key in self.keys() ]

   def iterkeys(self):
      return self.keys().__iter__()

   def itervalues(self):
      return items.__iter__()

   def keys(self):
      return list(self.linked)

   def __len__(self):
      return len(self.keys())

   def __repr__(self):
      return 'CControlDict(%s)' % dict([(key, value) for key, value in self.iteritems()])

   def setdefault(self, key, value=None):
      if key in self.keys():
         return self.__d.setdefault(key, value)
      else:
         raise KeyError, 'key not in linked object'

   def __setitem__(self, key, value):
      if key in self.keys():
         _c.ChangeDictCommand('Change item', self.__d, key, value)
      else:
         raise KeyError, 'CControldict: key not in linked object'

   def update(self, b):
      for key, value in b.iteritems():
         self.__setitem__(key, value)

   def values(self):
      return [ self.__getitem__(key) for key in self.keys() ]



# ---------------------------------------------------------------------------
#  Command controlled lists
# ---------------------------------------------------------------------------

class CList(object):
   '''A command handled list. This provides all the functionality of a python
   list, but any changes made to it are stored in the command system and can be
   bound to an event.
   All modifications to the list call one of the following methods:
   __delitem__
   insert
   __setitem__

   Sanity Check:
   >>> a = CList([1,2,3,4])
   >>> a == eval(repr(a))
   True
   >>> s = pickle.dumps(a)
   '''

   def __init__(self, *args, **kwargs):
      object.__init__(self)
      self.__l = list(*args, **kwargs)
      self.evt_deleting = Event()
      self.evt_inserting = Event()
      self.evt_setting = Event()
#      self.evt_after_set = Event()

   def __add__(self, other):
      return CList(self.__l + [i for i in other])

   def append(self, item):
      return self.insert(None, item)

   def __contains__(self, item):
      return item in self.__l

   def __delitem__(self, index):
      self.evt_deleting(index)
      _c.DeleteCommand('Delete Item', self.__l, index)
      (~self.evt_deleting)()

   def __eq__(self, var):
      return self.__l == var

   def __getitem__(self, index):
      return self.__l[index]

   def __iadd__(self, other):
      for item in other:
         self.append(item)

   def index(self, item):
      return self.__l.index(item)

   def insert(self, before, item):
      (self.evt_inserting)(before if before is not None else len(self))
      _c.AddCommand('Insert Item', self.__l, before, item)
      (~self.evt_inserting)()
      return item

   def __iter__(self):
      return self.__l.__iter__()

   def __len__(self):
      return len(self.__l)

   def __max__(self):
      return max(self.__l)

   def __min__(self):
      return min(self.__l)

   def __mul__(self, n):
      return CList(self.__l * n)

   def pop(self, index = -1):
      x = self.__getitem__(index)
      self.__delitem__(index)
      return x

   def radd(self, other):
      '''other + self'''
      return other + self.__l

   def remove(self, item):
      self.__delitem__(self.index(item))

   def __repr__(self):
      return 'CList(%s)' % repr(self.__l)

   def __setitem__(self, index, value):
         self.evt_setting(index, value)
         _c.ChangeCommand('Change item', self.__l, index, value)
         (~self.evt_setting)()


class CFilterList(object):
   '''A command handled list, which is a subset of another list. The interface
   is similar to that of a set, but with some modifications.'''

   def __init__(self, superlist, activeitems=None):
      self.__superlist = superlist
      self.__filter = CControlDict(superlist, lambda: False)
      if activeitems is not None:
         for i in len(self.__superlist):
            self.__filter[self.__superlist[i]] = activeitems[i]

   # Internal Use Only

   def _list(self):
      return filter(lambda item: self.__filter[item], self.__superlist)

   #Iterator access

   def __iter__(self):
      return self.__list().__iter__()

   # Item access

   def __getitem__(self, index):
      return self.__list()[index]

   def add(self, item):
      '''Add an item in the superlist to this list.'''
      self.__filter[item] = True

   def discard(self, index):
      '''Remove an item in the superlist from this list.'''
      self.__filter[index] = False
      
   def __len__(self):
      return len(self.__list())

   def __contains__(self, item):
      return item in self.__list()

   def __repr__(self):
      return repr([self.__filter[item] for item in self.__superlist])

class SubList(object):
   '''A list containing a subset of another list.  Essentially like 
   filter(func, superlist), but dynamic.
   Note that access functions (set, insert, del, etc) act on the superlist, so
   an added item may not necessarily appear in this list if it does not fulfil
   func().
   
   Be aware that the list composition should not change during iteration. This is
   difficult to achieve as external changes could change whether as item is included or not.
   '''
   
   def __init__(self, func, superlist):
      self.superlist = superlist
      self.func = func
      self.evt_deleting = Event()
      self.evt_inserting = Event()
      self.evt_setting = Event()
      
   def _mapindex(self, index):
      '''Maps an index to the corresponding superlist index.'''
      if index >= len(self):
         return len(self.superlist)
      else:
         return self.superlist.index(self.__sublist[index])
      
   @property
   def __sublist(self):
      return filter(self.func, self.superlist)
      
   def append(self, item):
      return self.superlist.insert(len(self), item)

   def __contains__(self, item):
      return item in self.__sublist

   def __delitem__(self, index):
      self.evt_deleting(index)
      del self.superlist[self._mapindex(index)]
      (~self.evt_deleting)()

   def __getitem__(self, index):
      return self.__sublist[index]

   def index(self, item):
      return self.__sublist.index(item)

   def insert(self, before, item):
      before = self._mapindex(before)
      (self.evt_inserting)(before if before is not None else len(self))
      self.superlist.insert(before, item)
      (~self.evt_inserting)()
      return item

   def __iter__(self):
      return self.__sublist.__iter__()

   def __len__(self):
      return len(self.__sublist)

   def __max__(self):
      return max(self.__sublist)

   def __min__(self):
      return min(self.__sublist)

   def pop(self, index = -1):
      x = self.__getitem__(index)
      self.__delitem__(index)
      return x

   def remove(self, item):
      self.__delitem__(self.index(item))

   def __setitem__(self, index, value):
      self.evt_setting(index, value)
      self.superlist[self._mapindex(index)] = value
      (~self.evt_setting)()

   
class CWeakList(object):
   '''A weakref list.

   e.g.
   >>> class A:
   ...   def __init__(self, var):
   ...     self.var = var
   ...   def __repr__(self):
   ...     return repr(self.var)
   ...
   >>> l = [A(1), A(2), A(3)]
   >>> a = CWeakList()
   >>> a.append(l[0])
   >>> a.append(l[1])
   >>> a.append(l[2])
   >>> len(a)
   3
   >>> a
   CWeakList([1, 2, 3])
   >>> del l[1]
   >>> a
   CWeakList([1, 3])
   >>> a.index(l[1])
   1
   >>> l.append(A(4))
   >>> a.append(l[-1])
   >>> a
   CWeakList([1, 3, 4])
   >>> a.index(a[1])
   1
   >>> a.index(l[-1])
   2
   '''

   def __init__(self):
      self.__l = CList()

   def append(self, item):
      self.insert(None, item)

   def __delcallback(self, item):
      self.__delitem__(self.index(item))

   def __contains__(self, item):
      return (item() if isinstance(item, ref) else item) in self.__l

   def __delitem__(self, index):
      _c.DeleteCommand('delete', self.__l, index)

   def __getitem__(self, index):
      return self.__l[index]

   def index(self, item):
      return self.__l.index(item if isinstance(item, ref) else ref(item))

   def insert(self, index, item):
      _c.AddCommand('append', self.__l, index, ref(item, self.__delcallback))

   def __len__(self):
      return len(self.__l)

   def remove(self, item):
      self.__delitem__(self.index(item))

   def __repr__(self):
      return 'CWeakList(%s)' % repr( [ i() for i in self.__l ] )

   def __setitem__(self, index, value):
      _c.ChangeCommand('set', self.__l, index, value if isinstance(value, ref) else ref(value))



class CCatList(object):
   '''A list consisting of a concatenation of other lists. The list can only be
   modified through modification of each sub-list, athough sub-lists can be
   added or removed.
   >>> a=CList([1,2,3])
   >>> b=CList([4,5,6])
   >>> cat = CCatList(a, b)
   >>> cat
   CCatList([1, 2, 3, 4, 5, 6])
   >>> a.append('a')
   'a'
   >>> cat
   CCatList([1, 2, 3, 'a', 4, 5, 6])
   >>> cat += CList('xy') # This will not work, because this is the only ref
   >>> cat.lists
   CWeakList([CList([1, 2, 3, 'a']), CList([4, 5, 6])])
   >>> c = CList('xy')
   >>> cat += c # This will work as the name c is another ref
   >>> cat
   CCatList([1, 2, 3, 'a', 4, 5, 6, 'x', 'y'])
   >>> cat.lists.remove(b)
   >>> cat
   CCatList([1, 2, 3, 'a', 'x', 'y'])
   '''

   def __init__(self, *args):
      object.__init__(self)
      self.lists = CWeakList()
      for arg in args:
         self.lists.append(arg)

   def fix(self):
      '''Return a single list containing the items.'''
      ret = []
      for l in range(len(self.lists)):
         ret += self.lists[l]()
      return ret

   def __contains__(self, item):
      return item in self.fix()

   def __getitem__(self, index):
      return self.fix()[index]

   def __iadd__(self, other):
      self.lists.append(other)
      return self

   def index(self, item):
      return self.fix().index(item)

   def __iter__(self):
      return self.fix().__iter__()

   def __len__(self):
      return len(self.fix())

   def __max__(self):
      return max(self.fix())

   def __min__(self):
      return min(self.fix())

   def __mul__(self, n):
      return CList(self.fix() * n)

   def __repr__(self):
      return 'CCatList(%s)' % repr(self.fix())


# ---------------------------------------------------------------------------
#  Properties
# ---------------------------------------------------------------------------

class CProperty(object):
   '''Defines a property with a default value a documentation. get and set are functions
   which should return the actual value to be used. Usually, they should not be instance methods
   as they may apply to a range of owner classes.'''

   def __init__(self, default, doc='', const=False, get=None, set=None):
      object.__init__(self)
      self.__d = WeakKeyDictionary()
      self.default = default
      self.__doc__ = doc
      self.__const = const
      self.evt_changing = Event()
      
      self.set = set
      self.get = get

   @property
   def const(self):
      return self.__const
      
   def __get__(self, instance, owner):
      if self.const and instance not in self.__d:
         return self.default
      else:
         value = self.__d.setdefault(instance, self.default)
         if self.get is None:
            return value
         else:
            return self.get(instance, owner, value)

   def __set__(self, instance, value):
      self.evt_changing()
      if self.const and instance in self.__d:
         warn('Cannot write to constant with id %s' % id(self))
      else:
         if self.set is not None:
            value = self.set(instance, value)
         if value is not None:
            _c.ChangeDictCommand('Change property', self.__d, instance, value)
      ~(self.evt_changing)()

class CEnum(CProperty):

   def __init__(self, *args, **kwargs):
      CProperty.__init__(self, *args, **kwargs)
      assert len(self.default) == 2, '%s should only contain two items' % self.default
      self.options = self.default[1]
      self.default = self.default[0]
      assert self.default in self.options, "'%s' not in %s" % (self.default, self.options)

   def __set__(self, instance, value):
      assert value in self.options, '%s not in enum' % value
      CProperty.__set__(self, instance, value)
      

# ---------------------------------------------------------------------------
#  Some useful property callback functions
# ---------------------------------------------------------------------------

class propset_type(object):
   '''set :  prop_settype(datatype)
   Use to specify a specific data type to convert values to.
   '''
   def __init__(self, datatype):
      self.datatype = datatype
      
   def __call__(self, instance, value):
      return value if isinstance(value, self.datatype) else self.datatype(value)
   
class PropDict(object):

   class __metaclass__(type):
      def __new__(cls, name, bases, cdict):
         cdict['properties'] = {}
         return type.__new__(cls, name, bases, cdict)

   def defprop(self, name, value=None):
      assert isinstance(value, CProperty), 'cannot assign %s to property' % type(value)
      # Make sure values already set in other objects are not overwritten
      if name not in self.__class__.__dict__['properties']:
         self.__class__.__dict__['properties'][name] = value
      
   def __getattr__(self, attr):
      if attr in self.__class__.__dict__['properties']:
         return self.__class__.__dict__['properties'][attr].__get__(self, self.__class__)
      else:
         raise AttributeError, attr

   def __setattr__(self, attr, value):
      if attr in self.__class__.__dict__['properties']:
         self.__class__.__dict__['properties'][attr].__set__(self, value)
      else:
         object.__setattr__(self, attr, value)
         
   def setprops(self, props):
      for key, value in props.iteritems():
         if isinstance(value, CProperty):
            self.defprop(key, value)
         elif key in self.__class__.__dict__['properties']:
            self.__class__.__dict__['properties'][key].__set__(self, value)


import doctest
doctest.testmod()
